package com.aikg.kgrag_java.utils;

import com.aikg.kgrag_java.pojo.vo.ObjectVO;
import io.minio.*;
import io.minio.Result;
import io.minio.errors.*;
import io.minio.http.Method;
import io.minio.messages.DeleteError;
import io.minio.messages.DeleteObject;
import io.minio.messages.Item;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Component
@Slf4j
public class MinioUtil {
    @Resource
    private MinioClient minioClient;

    /**
     * @param bucketName 桶名称
     * @param objectName 对象名称
     */
    public void deleteFile(String bucketName, String objectName) {
        try {
            minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * @param bucketName 名字
     * @return boolean
     * @Description : 判断bucket是否存在，不存在则创建
     */
    public boolean existBucket(String bucketName) {
        boolean exists;
        try {
            exists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
            if (!exists) {
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
                setBucketPublicReadPolicy(bucketName);
                exists = true;
            }
        } catch (Exception e) {
            e.printStackTrace();
            exists = false;
        }
        return exists;
    }

    /**
     * @param bucketName 存储bucket名称
     * @return {@link Boolean }
     * @Description 创建存储bucket
     */
    public Boolean makeBucket(String bucketName) {
        log.error("创建存储桶");

        try {
            minioClient.makeBucket(MakeBucketArgs.builder()
                    .bucket(bucketName)
                    .build());
        } catch (Exception e) {
            log.error("存储桶创建失败");
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * @param bucketName 存储bucket名称
     * @return {@link Boolean }
     * @Description 删除存储bucket
     */
    public Boolean removeBucket(String bucketName) {
        try {
            minioClient.removeBucket(RemoveBucketArgs.builder()
                    .bucket(bucketName)
                    .build());
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }


    /**
     * @param file       文件
     * @param objectName 对象名
     * @throws Exception
     * @Description 上传文件
     */
    public void upload(MultipartFile file, String objectName, String bucketName) throws Exception {
        Boolean aBoolean = existBucket(bucketName);
        if (!aBoolean) {
            log.info("存储桶不存在,并且创建失败");
        }
        InputStream inputStream = file.getInputStream();
        minioClient.putObject(PutObjectArgs.builder()
                .bucket(bucketName)
                .object(objectName)
                .stream(inputStream, file.getSize(), -1)
                .contentType(file.getContentType())
                .build());

    }

    /**
     * 设置桶为公共读取权限
     *
     * @param bucketName 存储桶名称
     * @throws Exception
     */
    public void setBucketPublicReadPolicy(String bucketName) throws Exception {
        String policy = "{\n" +
                "  \"Version\": \"2012-10-17\",\n" +
                "  \"Statement\": [\n" +
                "    {\n" +
                "      \"Effect\": \"Allow\",\n" +
                "      \"Principal\": \"*\",\n" +
                "      \"Action\": \"s3:GetObject\",\n" +
                "      \"Resource\": \"arn:aws:s3:::" + bucketName + "/*\"\n" +
                "    }\n" +
                "  ]\n" +
                "}";

        // 使用 MinIO 设置桶的访问策略
        minioClient.setBucketPolicy(SetBucketPolicyArgs.builder()
                .bucket(bucketName)
                .config(policy)
                .build());

        log.info("桶 {} 已设置为公共读取权限", bucketName);
    }

    /**
     * @param time     时间
     * @param timeUnit 时间单位
     * @return {@link String }
     * @Description 根据filename获取文件访问地址
     */
    public String getUrl(String bucketName, String objectName, int time, TimeUnit timeUnit) {
        String url = null;
        try {
            url = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                    .method(Method.GET)
                    .bucket(bucketName)
                    .object(objectName)
                    .expiry(time, timeUnit).build());
        } catch (ErrorResponseException | InsufficientDataException | InternalException | InvalidKeyException |
                 InvalidResponseException | IOException | NoSuchAlgorithmException | XmlParserException |
                 ServerException e) {
            e.printStackTrace();
        }
        return url;
    }

    /**
     * @param fileName
     * @return {@link ResponseEntity }<{@link byte[] }>
     * @Description description: 下载文件
     */
    public ResponseEntity<byte[]> download(String fileName, String bucketName) {
        ResponseEntity<byte[]> responseEntity = null;
        InputStream in = null;
        ByteArrayOutputStream out = null;
        try {
            in = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(fileName).build());
            out = new ByteArrayOutputStream();
            IOUtils.copy(in, out);
            //封装返回值
            byte[] bytes = out.toByteArray();
            HttpHeaders headers = new HttpHeaders();
            try {
                headers.add("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            headers.setContentLength(bytes.length);
            headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
            headers.setAccessControlExposeHeaders(Arrays.asList("*"));
            responseEntity = new ResponseEntity<byte[]>(bytes, headers, 200);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (in != null) {
                    try {
                        in.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (out != null) {
                    out.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return responseEntity;
    }


    /**
     * 创建文件夹或目录
     *
     * @param bucketName 存储桶
     * @param objectName 目录路径
     */
    public ObjectWriteResponse putDirObject(String bucketName, String objectName)
            throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, ErrorResponseException {
        return minioClient.putObject(
                PutObjectArgs.builder().bucket(bucketName).object(objectName).stream(
                                new ByteArrayInputStream(new byte[]{}), 0, -1)
                        .build());
    }

    /**
     * @param bucketName 存储桶名称
     * @param objectName 对象名称
     * @return boolean 文件是否存在
     * @Description 判断文件是否存在
     */
    public boolean isFileExist(String bucketName, String objectName) {
        try {
            // 获取文件的元数据，如果文件不存在会抛出异常
            minioClient.statObject(StatObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName).build());
            return true;  // 文件存在
        } catch (ErrorResponseException e) {
            if (e.errorResponse().code().equals("NoSuchKey")) {
                return false;  // 文件不存在
            }
            throw new RuntimeException("Error checking file existence", e);  // 其他异常
        } catch (Exception e) {
            throw new RuntimeException("Error checking file existence", e);
        }
    }

    /**
     * 批量删除文件
     *
     * @param bucketName 存储桶
     * @param keys       需要删除的文件列表
     * @return
     */
    public void removeFiles(String bucketName, List<String> keys) {
        List<DeleteObject> objects = new LinkedList<>();
        keys.forEach(s -> {
            objects.add(new DeleteObject(s));
            try {
                removeFile(bucketName, s);
            } catch (Exception e) {
                log.error("批量删除失败！error:{}", e);
            }
        });
    }

    /**
     * 删除文件
     *
     * @param bucketName 存储桶
     * @param objectName 文件名称
     */
    public void removeFile(String bucketName, String objectName) throws Exception {
        minioClient.removeObject(
                RemoveObjectArgs.builder()
                        .bucket(bucketName)
                        .object(objectName)
                        .build());
    }

    /**
     * 查找某个桶下某个文件夹下的所有文件
     *
     * @param bucketName 桶名
     * @param folder     文件夹名
     * @return List<ObjectVO> 文件对象列表
     * @throws IOException
     */
    public List<ObjectVO> listObjects(String bucketName, String folder) {
        Iterable<Result<Item>> results = minioClient.listObjects(
                ListObjectsArgs.builder().bucket(bucketName).prefix(folder + "/").delimiter("/").build());

        List<ObjectVO> objectVOS = new ArrayList<>();
        for (Result<Item> result : results) {
            Item item = null;
            try {
                item = result.get();
            } catch (ErrorResponseException | InsufficientDataException | InternalException | InvalidKeyException |
                     InvalidResponseException | XmlParserException | NoSuchAlgorithmException | ServerException |
                     IOException e) {
                throw new RuntimeException(e);
            }
            ObjectVO objectVO = ObjectVO.fromItem(item);
            objectVOS.add(objectVO);
        }
        return objectVOS;
    }

    /**
     * 查所有文件夹（knowledgedb）
     *
     * @param bucketName 桶名
     * @return
     */
    public List<String> listFolders(String bucketName) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        Iterable<Result<Item>> results = minioClient.listObjects(
                ListObjectsArgs.builder().bucket(bucketName).build());

        List<String> knowledgedb = new ArrayList<>();
        for (Result<Item> result : results) {
            String s = null;
            try {
                String objectName = result.get().objectName();
                s = objectName.endsWith("/") ? objectName.substring(0, objectName.length() - 1) : objectName;
            } catch (ErrorResponseException | InsufficientDataException | InternalException | InvalidKeyException |
                     InvalidResponseException | XmlParserException | NoSuchAlgorithmException | ServerException |
                     IOException e) {
                throw new RuntimeException(e);
            }
            knowledgedb.add(s);
        }

        return knowledgedb;
    }


    /**
     * 删除桶里面所有文件
     *
     * @param bucketName 桶名
     */
    public boolean deleteAllFilesByBucketName(String bucketName) {

        try {
            // 检查桶是否存在，如果不存在直接返回
            if (!minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())) {
                return true;
            }

            //获取所有knowledgedb库
            Iterable<Result<Item>> knowledgedbs = minioClient.listObjects(
                    ListObjectsArgs.builder().bucket(bucketName).build());

            List<String> knowledgedb = new ArrayList<>();
            for (Result<Item> result : knowledgedbs) {
                String objectName = result.get().objectName();
                knowledgedb.add(objectName);
            }

            //删除每个knowledgedb进行整体删除
            for (String folderPath : knowledgedb) {

                // 获取该文件夹下的所有对象（包括文件夹内的所有文件）
                List<DeleteObject> deleteObjects = new ArrayList<>();
                ListObjectsArgs listObjectsArgs = ListObjectsArgs.builder()
                        .bucket(bucketName)  // 确保桶名正确
                        .prefix(folderPath)  // 仅列出文件夹中的对象
                        .build();

                // 获取对象列表
                Iterable<Result<Item>> results = minioClient.listObjects(listObjectsArgs);

                // 将符合条件的对象添加到删除列表
                for (Result<Item> result : results) {
                    Item item = result.get();
                    deleteObjects.add(new DeleteObject(item.objectName()));  // 添加到删除对象列表
                }

                if (!deleteObjects.isEmpty()) {
                    // 批量删除对象
                    RemoveObjectsArgs removeObjectsArgs = RemoveObjectsArgs.builder()
                            .bucket(bucketName)  // 确保桶名正确
                            .objects(deleteObjects)
                            .build();

                    Iterable<Result<DeleteError>> removeResults = minioClient.removeObjects(removeObjectsArgs);

                    // 检查删除结果
                    for (Result<DeleteError> removeResult : removeResults) {
                        DeleteError deleteError = removeResult.get();
                        log.info("删除文件错误:"+deleteError.objectName());
                    }

                    log.info("在文件夹："+folderPath+"中删除了所有文件");
                } else {
                    log.info("文件夹："+folderPath+"中没有文件可以删除");
                }
            }
            // 删除桶
            return removeBucket(bucketName);
        } catch (ErrorResponseException | InsufficientDataException | InvalidKeyException | IOException |
                 NoSuchAlgorithmException | InvalidResponseException | InternalException | ServerException |
                 XmlParserException e) {
            throw new RuntimeException(e);
        }
    }
}
